import { dom, EventHandler, Nodes } from '@knno/dom';
import { Container } from '../interface';
import { tabStyle } from './tab.css';
import { hideScrollBar } from '../global.css';
import { isDescendant } from '../../tools/utils';
import SVGMenu from '@material-design-icons/svg/filled/menu.svg';
import { PopList, SelectHandler } from '../popList/popList';
import SVGClose from '@material-design-icons/svg/filled/close.svg';
import { DATA } from '../symbol';

interface ActiveTabEventDetail {
	page: number;
}

type ActiveTabEvent = CustomEvent<ActiveTabEventDetail>;

export interface TabEventMap {
	tabactive: ActiveTabEvent;
}
export type TabEventKey = keyof TabEventMap;
export type TabEventHandler<T extends TabEventKey, O extends Nodes> = (this: O, event: TabEventMap[T]) => void;

export interface Tab {
	on<K extends keyof TabEventMap>(
		event: K,
		listener: TabEventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
	on<K extends keyof HTMLElementEventMap>(
		event: K,
		listener: EventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
	off<K extends keyof TabEventMap>(
		event: K,
		listener: TabEventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
	off<K extends keyof HTMLElementEventMap>(
		event: K,
		listener: EventHandler<K, this>,
		options?: boolean | AddEventListenerOptions
	): this;
}

type TabItem = { text?: string; icon?: string; disabled?: boolean; closable?: boolean; custom?: (() => Nodes) | Nodes };

export type TabDef = string | TabItem;

export class Tab extends Container {
	private [DATA]: {
		arr: TabDef[];
		head: Nodes;
		box: Nodes;
		btn: Nodes;
		act: number;
		pop: SelectHandler | null;
		sheltered: (TabItem & { index: number })[];
		pad: number;
		autoHideTabs: boolean;
	};

	protected get box(): Nodes {
		return this[DATA].box;
	}

	constructor() {
		const box = dom.div('body');
		const head = dom.div('head', hideScrollBar);
		const root = dom.div(tabStyle).append(head, box);
		const menuBtn = dom.button('menu').html(SVGMenu);
		super(root);
		this[DATA] = {
			arr: [],
			head,
			box,
			btn: menuBtn,
			act: 0,
			pop: null,
			sheltered: [],
			pad: 0,
			autoHideTabs: false,
		};
		// this[BOX] = box;
		// this[HEAD] = head;
		// this[BTN] = menuBtn;
		const observer = new MutationObserver(() => {
			this.showPage();
		});
		menuBtn.on('click', (event) => {
			event.stopPropagation();
			this[DATA].pop = PopList.show(
				this[DATA].sheltered,
				{ text: 'text' },
				{
					refer: menuBtn,
					disableField: 'disabled',
					popupPosition: 'bottom-right',
					width: 200,
					focus: true,
				}
			)
				.onClose(() => {
					this[DATA].pop = null;
				})
				.onSelect((_, __, row) => {
					this.active(this[DATA].sheltered[row].index);
				});
		});
		head.on('wheel', (event) => {
			if (event.deltaY != 0) {
				const v = event.deltaY / Math.abs(event.deltaY);
				event.preventDefault();
				const div = head.elems()[0] as HTMLDivElement;
				div.scrollBy(v * 20, 0);
			}
		});
		head.on('click', (event) => {
			const target = event.target as HTMLElement;
			const elems = this[DATA].head.children().elems();
			for (let i = 0; i < elems.length; i++) {
				const elem = elems[i];
				if (isDescendant([elem], target)) {
					if (target.getAttribute('name') === 'close') {
						const newTabs = this.tabs().filter((_, idx) => idx != i);
						this.tabs(newTabs);
						dom.wrap(box.children().elems()[i] ?? []).detach();
						event.stopPropagation();
					} else if (!dom.wrap(elem).class().includes('disable')) {
						this.active(i);
						this.emit(new CustomEvent('tabactive', { detail: { page: i } }));
					}
					break;
				}
			}
		});
		observer.observe(box.elems()[0], { subtree: false, childList: true, attributes: false, characterData: false });
		this.showPage();
		this.renderHead();
		const resizeObserver = new ResizeObserver(() => {
			this.renderHead();
		});
		resizeObserver.observe(this.elems()[0]);
	}

	private showPage() {
		if (this[DATA].act < 0) {
			this[DATA].act = 0;
		}
		if (this[DATA].act > this[DATA].arr.length - 1) {
			this[DATA].act = this[DATA].arr.length - 1;
		}
		this[DATA].box.children().forEach((elem, idx) => {
			if (idx === this[DATA].act) {
				dom.wrap(elem).addClass('active');
			} else {
				dom.wrap(elem).removeClass('active');
			}
		});
	}

	private isTabSheltered(el: HTMLElement): boolean {
		return (
			el.offsetLeft + el.offsetWidth > this[DATA].head.innerWidth() - (this[DATA].pad === 0 ? 0 : this[DATA].pad / 2)
		);
	}

	private showHideTabs() {
		if (this[DATA].autoHideTabs) {
			if (this[DATA].arr.length <= 1) {
				this[DATA].head.addClass('hidden');
			} else {
				this[DATA].head.removeClass('hidden');
			}
		} else {
			this[DATA].head.removeClass('hidden');
		}
	}

	private renderHead() {
		const al = this[DATA].arr.length;
		const hl = this[DATA].head.children().length;
		for (let i = 0; i < al - hl; i++) {
			this[DATA].head.append(dom.button());
		}
		for (let i = 0; i < hl - al; i++) {
			if (this[DATA].head.lastChild()[0] !== this[DATA].btn[0]) {
				this[DATA].head.remove(this[DATA].head.lastChild());
			}
		}
		this[DATA].sheltered = [];
		this[DATA].arr.forEach((page, idx) => {
			let item: TabItem;
			if (typeof page === 'string') {
				item = { text: page };
			} else {
				item = page;
			}
			const label = this[DATA].head.child(idx);
			label.html('');
			if (item.icon) {
				label.append(dom.html(item.icon));
			}
			if (item.text || !item.custom) {
				label.append(dom.text(item.text ?? idx + 1 + ''));
			}
			if (typeof item.custom === 'function') {
				label.append(item.custom());
			} else if (item.custom instanceof Nodes) {
				label.append(item.custom);
			}
			if (item.closable) {
				label.append(dom.html(SVGClose).attr('name', 'close'));
			}
			if (idx === this[DATA].act) {
				label.addClass('active');
				label.removeClass('disable');
			} else {
				label.removeClass('active');
				if (item.disabled) {
					label.addClass('disable');
				} else {
					label.removeClass('disable');
				}
			}
			const isSheltered = this.isTabSheltered(label.elems()[0] as HTMLElement);
			if (isSheltered) {
				this[DATA].sheltered.push({ ...item, index: idx });
			}
		});

		if (this[DATA].sheltered.length > 0) {
			this[DATA].head.addClass('menu');
			this[DATA].head.append(this[DATA].btn);
			this[DATA].pad = this[DATA].btn.width();
			if (this[DATA].pop) {
				this[DATA].pop.list.rows(this[DATA].sheltered);
			}
		} else {
			this[DATA].pad = 0;
			this[DATA].head.removeClass('menu');
			this[DATA].btn.detach();
			if (this[DATA].pop) {
				this[DATA].pop.close();
			}
		}
		this.showHideTabs();
	}

	autoHideTabs(): boolean;
	autoHideTabs(value: boolean): this;
	autoHideTabs(value?: boolean): this | boolean {
		if (value === undefined) return this[DATA].autoHideTabs;
		this[DATA].autoHideTabs = !!value;
		this.showHideTabs();
		return this;
	}

	tabs(): TabDef[];
	tabs(value: TabDef[]): this;
	tabs(value?: TabDef[]): TabDef[] | this {
		if (value === undefined) {
			return this[DATA].arr;
		}
		this[DATA].arr = value;
		this.showPage();
		this.renderHead();
		return this;
	}

	active(): number;
	active(value: number): this;
	active(value?: number): number | this {
		if (value === undefined) {
			return this[DATA].arr.length === 0 ? -1 : this[DATA].act;
		}
		this[DATA].act = value;
		this.showPage();
		this.renderHead();
		const activeBtn = this[DATA].head.query('button.active');
		if (activeBtn.left() < this[DATA].head.scrollLeft()) {
			this[DATA].head.scrollLeft(activeBtn.left());
		}
		if (
			this[DATA].head.scrollLeft() <
			activeBtn.left() + activeBtn.width() - (this[DATA].head.innerWidth() - this[DATA].pad)
		) {
			this[DATA].head.scrollLeft(
				activeBtn.left() + activeBtn.width() - (this[DATA].head.innerWidth() - this[DATA].pad)
			);
		}
		return this;
	}
}

export function tab(...className: string[]): Tab {
	return new Tab().addClass(...className);
}
